import { ColumnMetadata } from "./ColumnMetadata"
import { EntityMetadata } from "./EntityMetadata"
import { NamingStrategyInterface } from "../naming-strategy/NamingStrategyInterface"
import { DeferrableType } from "./types/DeferrableType"
import { OnDeleteType } from "./types/OnDeleteType"
import { OnUpdateType } from "./types/OnUpdateType"

/**
 * Contains all information about entity's foreign key.
 */
export class ForeignKeyMetadata {
    // -------------------------------------------------------------------------
    // Public Properties
    // -------------------------------------------------------------------------

    /**
     * Entity metadata where this foreign key is.
     */
    entityMetadata: EntityMetadata

    /**
     * Entity metadata which this foreign key references.
     */
    referencedEntityMetadata: EntityMetadata

    /**
     * Array of columns of this foreign key.
     */
    columns: ColumnMetadata[] = []

    /**
     * Array of referenced columns.
     */
    referencedColumns: ColumnMetadata[] = []

    /**
     * What to do with a relation on deletion of the row containing a foreign key.
     */
    onDelete?: OnDeleteType

    /**
     * What to do with a relation on update of the row containing a foreign key.
     */
    onUpdate?: OnUpdateType

    /**
     * When to check the constraints of a foreign key.
     */
    deferrable?: DeferrableType

    /**
     * Gets the table name to which this foreign key is referenced.
     */
    referencedTablePath: string

    /**
     * Gets foreign key name.
     * If unique constraint name was given by a user then it stores givenName.
     * If unique constraint name was not given then its generated.
     */
    name: string

    /**
     * Gets array of column names.
     */
    columnNames: string[] = []

    /**
     * Gets array of referenced column names.
     */
    referencedColumnNames: string[] = []

    /**
     * User specified unique constraint name.
     */
    givenName?: string

    // ---------------------------------------------------------------------
    // Constructor
    // ---------------------------------------------------------------------

    constructor(options: {
        entityMetadata: EntityMetadata
        referencedEntityMetadata: EntityMetadata
        namingStrategy?: NamingStrategyInterface
        columns: ColumnMetadata[]
        referencedColumns: ColumnMetadata[]
        onDelete?: OnDeleteType
        onUpdate?: OnUpdateType
        deferrable?: DeferrableType
        name?: string
    }) {
        this.entityMetadata = options.entityMetadata
        this.referencedEntityMetadata = options.referencedEntityMetadata
        this.columns = options.columns
        this.referencedColumns = options.referencedColumns
        this.onDelete = options.onDelete || "NO ACTION"
        this.onUpdate = options.onUpdate || "NO ACTION"
        this.deferrable = options.deferrable
        this.givenName = options.name
        if (options.namingStrategy) this.build(options.namingStrategy)
    }

    // ---------------------------------------------------------------------
    // Public Methods
    // ---------------------------------------------------------------------

    /**
     * Builds some depend foreign key properties.
     * Must be called after all entity metadatas and their columns are built.
     */
    build(namingStrategy: NamingStrategyInterface) {
        this.columnNames = this.columns.map((column) => column.databaseName)
        this.referencedColumnNames = this.referencedColumns.map(
            (column) => column.databaseName,
        )
        this.referencedTablePath = this.referencedEntityMetadata.tablePath
        this.name = this.givenName
            ? this.givenName
            : namingStrategy.foreignKeyName(
                  this.entityMetadata.tableName,
                  this.columnNames,
                  this.referencedEntityMetadata.tableName,
                  this.referencedColumnNames,
              )
    }
}
